package plus.run;

import java.io.*;
import java.util.*;

import plus.B;
import plus.DynamicExtent;
import plus.Gripe;
import plus.M_System;
import plus.Module;
import plus.Parse;
import plus.Scan;
import plus.VScope;
import plus.Parse.Error;
import plus.decl.Unit;

public final class Loader {
	static Set<File> red = new HashSet<File>();
	static Map<File, Module> loaded = new HashMap<File, Module>();
	static List<Module> initQueue = new LinkedList<Module>();
	public static File __FILE__;
	
	public static M_System builtin = new B();
	public static void init() {
		red.clear();
		loaded.clear();
		initQueue.clear();
		builtin.install();
		initQueue.add(builtin);
		VScope.enterProc(Loader.global);
	}
	
	public static Unit loadFile(File path) {
		Scan scan;
		try {
			__FILE__ = path.getCanonicalFile();
			red.add(__FILE__);
			scan = new Scan(new FileReader(__FILE__));
		} catch (IOException e) {
			throw new Gripe(0, e.getMessage()+" while trying to load "+path);
		}
		try {
			Unit unit = scan.scan();
			unit.resolveImports();
			return unit;
		} catch (plus.Parse.Error e) {
			throw new Gripe(scan.line, "Unexpected "+messageFor(e, scan));
		} catch (IOException e) {
			throw new Gripe(scan.line, e.getMessage());
		}
	}
	private static String messageFor(Error e, Scan scan) {
		if (e.type >=0 ) return("token: "+Parse.tokens[e.type]+" "+scan.text());
		else return String.format("character (%s)", repr(-e.type));
	}

	private static String repr(int i) {
		if (i>=32) return String.format("'%c'", i);
		else return String.format("$%02x", i);
	}

	public static Module beckonSystemModule(String path) {
		assert path != null;
		throw new Gripe("No <system> modules available at this time.");
		// TODO these would presumably attach to the VM as they are loaded.
	}

	public static Module beckonUserModule(File base, String path) {
		assert base != null;
		assert path != null;
		// Figure out the correct path from which to parse a module:
		File target = new File(base.getParentFile(), path);
		if (red.contains(target)) {
			// Presumably we're on an infinite loop.
			throw new Gripe("Module import loop detected: "+red);
		}
		if (! loaded.containsKey(target)) {
			Unit unit = loadFile(target);
			Module mod = unit.asModule();
			loaded.put(target, mod);
			red.remove(target);
			mod.install();
			initQueue.add(mod);
		}
		return loaded.get(target);
	}

	public static DynamicExtent global = new DynamicExtent(0);
	
	public static void compileLoadingSequence() {
//		System.err.println("Compiling loading sequence.");
		global.compileEnter();
		for (Module m: initQueue) m.compileStartup();
	}
}
